package com.isesol.orm.jpa;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.isesol.arch.common.utils.reflect.ReflectionUtils;
import com.isesol.orm.jpa.query.DynamicSpecifications;
import com.isesol.orm.jpa.query.SearchFilter;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

public abstract class AbstractJpaEntityManager<T extends BaseIdEntity<EIT>, EIT extends Serializable> {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    // -- 私有属性 --//
    private Class<?> entityClazz;
    private Class<?> entityKeyClazz;

    protected JpaDao dao;

    public AbstractJpaEntityManager() {
        entityClazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
        entityKeyClazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
    }

    public T get(EIT id) {
        return (T) dao.findOne(id);
    }

    protected void beforeSave(T entity) {}

    protected void afterSave(T entity) {}

    @Transactional(readOnly = false)
    public void save(Iterable<T> entities) {
        dao.save(entities);
    }

    @Transactional(readOnly = false)
    public void save(T entity) {
        beforeSave(entity);
        dao.save(entity);
        afterSave(entity);
    }

    protected void beforeDelete(EIT id) {}

    protected void afterDelete(EIT id) {}

    @Transactional(readOnly = false)
    public void delete(EIT id) {
        beforeDelete(id);
        dao.delete(id);
        afterDelete(id);
    }

    @Transactional(readOnly = false)
    public void batchDelete(String ids) {
        String[] split = StringUtils.split(ids, ",");
        for (String s : split) {
            EIT rid = (EIT) ConvertUtils.convert(s, entityKeyClazz);
            delete(rid);
        }
    }

    public T findOne(List<SearchFilter> filters) {
        Specification<T> spec = DynamicSpecifications.bySearchFilter(filters, "AND");
        return (T) dao.findOne(spec);
    }

    public T findOne(SearchFilter...filters) {
        List<SearchFilter> filtersList = Arrays.asList(filters);
        return findOne(filtersList);
    }

    public long count(List<SearchFilter> filters) {
        Specification<T> spec = DynamicSpecifications.bySearchFilter(filters, "AND");
        return dao.count(spec);
    }

    public T findUniqueBy(String fieldName, Object value) {
        List<SearchFilter> filters = new ArrayList<SearchFilter>();
        filters.add(new SearchFilter(fieldName, SearchFilter.Operator.EQ, value));
        return findOne(filters);
    }

    public List<T> findBy(String fieldName, Object value) {
        List<SearchFilter> filters = new ArrayList<SearchFilter>();
        filters.add(new SearchFilter(fieldName, SearchFilter.Operator.EQ, value));
        return find(filters);
    }

    public List<T> findBy(String fieldName, Object value, Sort sort) {
        List<SearchFilter> filters = new ArrayList<SearchFilter>();
        filters.add(new SearchFilter(fieldName, SearchFilter.Operator.EQ, value));
        return find(filters, sort);
    }

    public List<T> find(SearchFilter...filters) {
        List<SearchFilter> filtersList = Arrays.asList(filters);
        return find(filtersList);
    }

    public List<T> find(List<SearchFilter> filters) {
        return find(filters, "AND");
    }

    public List<T> find(List<SearchFilter> filters, Sort sort) {
        Specification<T> spec = DynamicSpecifications.bySearchFilter(filters, "AND");
        return dao.findAll(spec, sort);
    }

    public List<T> find(List<SearchFilter> filters, String groupOp) {
        Specification<T> spec = DynamicSpecifications.bySearchFilter(filters, groupOp);
        List<T> all = dao.findAll(spec);
        return all;
    }

    @Transactional(readOnly = true)
    public List<T> findAll() {
        return find(new ArrayList<SearchFilter>());
    }

    @Transactional(readOnly = true)
    public Iterable<T> findAll(Iterable<EIT> ids) {
        return dao.findAll(ids);
    }

    @Transactional(readOnly = true)
    public Iterable<T> findAll(Sort sort) {
        return dao.findAll(sort);
    }

    /**
     * 分页查询
     * @param page
     * @return
     */
    public Page<T> findPage(List<SearchFilter> filters, Pageable page) {
        Specification<T> spec = DynamicSpecifications.bySearchFilter(filters, "AND");
        return dao.findAll(spec, page);
    }

    protected abstract void setDao(JpaDao dao);

}
